Learning rust
Notes to self about learning rust
The take aways from The book on rust that took my knowledge from point A to B as primarily a Python developer.
Chapter 2 - Programming a Guessing Game
Declaring variales
Let
https://doc.rust-lang.org/stable/book/ch02-00-guessing-game-tutorial.html#:~:text=let%20statement
let declares a variable in the local scope
variables are immutable by default
::
"The :: syntax in the ::new line indicates that new is an associated function of the String type"
Method naming
in object oriented we call a function on an object a method
Clone
In rust you can reference the same object, and do a object.clone, similar to python
String
use std::io;
fn main() {
println!("Guess the number!");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
println!("You guessed: {guess}");
}
I'm learning rust, why do we instantiate a variable with `String::new();` only to append something to it afterwards, is it functional or for legibility, reply concisely.
In Rust, `String::new()` is the common “start with an empty owned buffer” pattern. read_line(&mut guess) needs a String you can mutate (grow) as input arrives, so you create an empty one first.
It’s functional (you need an owned, mutable String to pass to read_line), and also idiomatic/legible because it makes the intent—“this will hold the user’s input”—clear.
Result enums
Result enum states are called variants
Rust doesn’t yet include random number functionality in its standard library
Chapter 3 - Common Programming Concepts
Looping chars
fn main() {
for n in 1..u32::MAX {
let c: char = match char::from_u32(n) {
Some(j) => j,
None => {
// println!("Can't get number for char value {n}");
continue;
}
};
// print!(" {n} {c}");
print!("{c}");
}
}
Keeps trying with ints that don't have a unicode char
The rust book states: "Unicode scalar values range from U+0000 to U+D7FF and U+E000 to U+10FFFF inclusive"
Coming back to this after the end of chapter 3:
fn main() {
for n in 1.. {
let c: char = char::from_u32(n).expect(&format!("Can't get number for char value {n}"));
print!("{c}");
}
}
thread 'main' (177732) panicked at src/main.rs:6:17:
Can't get number for char value 55296
fn print(char: u32) {
let char: char = char::from_u32(char).expect(&format!("Can't get number for char value {char}"));
print!("{char}");
}
fn main() {
for n in 0..0xD7FF {
print(n);
}
for n in 0xE000..0x10FFFF {
print(n);
}
}
Finally writing some rust i must admit that i find rusts compiler errors are really friendly, the syntax is plesent and legible, and leads you to handle errors.
Assignments
Temperature converter
use std::io;
fn celsius_to_fahrenheit(celsius: f64) -> f64 {
(celsius * 1.8) + 32.0
}
fn fahrenheit_to_celsius(fahrenheit: f64) -> f64 {
(fahrenheit - 32.0) / 1.8
}
fn main() {
println!("Input temperature in the format: <number><unit> e.g: 22C or 30.4F");
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("Failed to read line");
let (temp, unit) = input.trim().split_at(input.len() - 2);
let unit = unit.to_uppercase();
let temp: f64 = temp.parse().expect(&format!("Couldn't parse number from: {temp}"));
println!("temp: {temp}, unit: {unit}");
let result_unit: char;
let result: f64 = match unit.as_str() {
"F" => {
result_unit = 'C';
fahrenheit_to_celsius(temp)
}
"C" => {
result_unit = 'F';
celsius_to_fahrenheit(temp)
}
_ => panic!("Unknown unit {unit}"),
};
println!("{temp}°{unit} = {result}°{result_unit}")
}
Asking an ai bot to give feedback
I'm learning rust, coming from python and some C programming, I've read the first 3 chapters of the rust book, which covers:
...
Your code is solid Rust and it mostly avoids common beginner pitfalls from the book—but there are a few correctness/robustness issues and a couple “Rust-idiomatic” improvements.
It validates me, wow, what a supprise 
1. split_at(input.len() - 2) is brittle and can be incorrect because you compute the index from the untrimmed string length (so whitespace/newline can break the split).
2. Slicing by byte index is unsafe for general cases; indexing must land on a valid char boundary (your current approach assumes a fixed layout).
3. Parsing by “last 2 characters” assumes the unit is always exactly one character and located at a fixed position; inputs like 22 C, missing unit, etc., will fail or behave unexpectedly.
4. Your match works, but using result_unit + mutation in match arms is less idiomatic; returning both values directly is cleaner.
5. Using panic!/expect for invalid user input is fine for an exercise, but for real programs you’d prefer returning Result and printing a friendly error.
If you want a robust fix: parse from let trimmed = input.trim(); and take the last non-whitespace character as the unit, then parse the remaining prefix as the number.
Nothing major, but let's address it to learn.
diff --git a/src/main.rs b/src/main.rs index 9fee6af..2771e71 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,23 +15,17 @@ fn main() { io::stdin() .read_line(&mut input) .expect("Failed to read line"); + input = input.trim().to_string(); - let (temp, unit) = input.trim().split_at(input.len() - 2); + let (temp, unit) = input.split_at(input.len() - 1); let unit = unit.to_uppercase(); let temp: f64 = temp.parse().expect(&format!("Couldn't parse number from: {temp}")); println!("temp: {temp}, unit: {unit}"); - let result_unit: char; - let result: f64 = match unit.as_str() { - "F" => { - result_unit = 'C'; - fahrenheit_to_celsius(temp) - } - "C" => { - result_unit = 'F'; - celsius_to_fahrenheit(temp) - } + let (result_unit, result) = match unit.as_str() { + "F" => ('C', fahrenheit_to_celsius(temp)), + "C" => ('F', celsius_to_fahrenheit(temp)), _ => panic!("Unknown unit {unit}"), }; println!("{temp}°{unit} = {result}°{result_unit}")
Fibonacci
fn main() {
const iterations: usize = 20;
let mut prev: usize = 0;
let mut cur: usize = 1;
for _ in 0..iterations - 1 {
let temp: usize = cur;
cur = cur + prev;
println!("{prev} + {temp} = {cur}");
prev = temp;
}
println!("{iterations}'th fibonacci number is: {cur}");
}
$ cargo run Compiling fibonacci v0.1.0 (/home/user/projects/my_misc/rust/learning/3/fibonacci) warning: constant `iterations` should have an upper case name --> src/main.rs:2:11 | 2 | const iterations: usize = 20; | ^^^^^^^^^^ | = note: `#[warn(non_upper_case_globals)]` (part of `#[warn(nonstandard_style)]`) on by default help: convert the identifier to upper case | 2 - const iterations: usize = 20; 2 + const ITERATIONS: usize = 20; | $ cargo fix --bin "fibonacci" -p fibonacci warning: `fibonacci` (bin "fibonacci") generated 1 warning (run `cargo fix --bin "fibonacci" -p fibonacci` to apply 1 suggestion) Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.28s Running `target/debug/fibonacci` error: the working directory of this package has uncommitted changes, and `cargo fix` can potentially perform destructive changes; if you'd like to suppress this error pass `--allow-dirty`, or commit the changes to these files: * .gitignore (dirty) * Cargo.lock (dirty) * Cargo.toml (dirty) * src/ (dirty) $ cargo fix --bin "fibonacci" -p fibonacci --allow-dirty Checking fibonacci v0.1.0 (/home/user/projects/my_misc/rust/learning/3/fibonacci) Fixed src/main.rs (1 fix) Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.13s
Again, really nice and native tooling
fn main() {
const ITERATIONS: usize = 20;
let mut prev: usize = 0;
let mut cur: usize = 1;
for _ in 0..ITERATIONS - 1 {
let temp: usize = cur;
cur = cur + prev;
println!("{prev} + {temp} = {cur}");
prev = temp;
}
println!("{ITERATIONS}'th fibonacci number is: {cur}");
}
$ cargo run
0 + 1 = 1
1 + 1 = 2
1 + 2 = 3
2 + 3 = 5
3 + 5 = 8
5 + 8 = 13
8 + 13 = 21
13 + 21 = 34
21 + 34 = 55
34 + 55 = 89
55 + 89 = 144
89 + 144 = 233
144 + 233 = 377
233 + 377 = 610
377 + 610 = 987
610 + 987 = 1597
987 + 1597 = 2584
1597 + 2584 = 4181
2584 + 4181 = 6765
20'th fibonacci number is: 6765
Correct
Currious, if i keep running it, i outgrow the instruction set of usize on x86_64,
19.740.274.219.868.223.167 > 2^64 (18,446,744,073,709,551,616)
89 1779979416004714189 + 2880067194370816120 = 4660046610375530309
90 2880067194370816120 + 4660046610375530309 = 7540113804746346429
91 4660046610375530309 + 7540113804746346429 = 12200160415121876738
thread 'main' (266042) panicked at src/main.rs:9:15:
attempt to add with overflow
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
>>> 7540113804746346429 + 12200160415121876738
19740274219868223167
Python don't care, will have to keep that in mind. Useful for efficiency and embedded systems.
Twelve days of christmas
const DAYS: [&str; 12] = ["first", "second", "third", "fourth", "fifth", "sixth",
"seventh","eighth", "ninth", "tenth", "eleventh", "twelfth"];
const THINGS: [&str; 12] = [
"A partridge in a pear tree",
"Two turtle doves and",
"Three french hens",
"Four calling birds",
"Five golden rings",
"Six geese a-laying",
"Seven swans a-swimming",
"Eight maids a-milking",
"Nine ladies dancing",
"Ten lords a-leaping",
"Eleven pipers piping",
"Twelve drummers drumming"
];
fn main() {
for i in 0..12 {
println!("On the {} day of Christmas, my true love sent to me", DAYS[i]);
for j in (0..i + 1).rev() {
println!("{}", THINGS[j]);
}
println!();
}
}
On the first day of Christmas, my true love sent to me
A partridge in a pear tree
On the second day of Christmas, my true love sent to me
Two turtle doves and
A partridge in a pear tree
On the third day of Christmas, my true love sent to me
Three french hens
Two turtle doves and
A partridge in a pear tree
On the fourth day of Christmas, my true love sent to me
Four calling birds
Three french hens
Two turtle doves and
A partridge in a pear tree
On the fifth day of Christmas, my true love sent to me
Five golden rings
Four calling birds
Three french hens
Two turtle doves and
A partridge in a pear tree
On the sixth day of Christmas, my true love sent to me
Six geese a-laying
Five golden rings
Four calling birds
Three french hens
Two turtle doves and
A partridge in a pear tree
On the seventh day of Christmas, my true love sent to me
Seven swans a-swimming
Six geese a-laying
Five golden rings
Four calling birds
Three french hens
Two turtle doves and
A partridge in a pear tree
On the eighth day of Christmas, my true love sent to me
Eight maids a-milking
Seven swans a-swimming
Six geese a-laying
Five golden rings
Four calling birds
Three french hens
Two turtle doves and
A partridge in a pear tree
On the ninth day of Christmas, my true love sent to me
Nine ladies dancing
Eight maids a-milking
Seven swans a-swimming
Six geese a-laying
Five golden rings
Four calling birds
Three french hens
Two turtle doves and
A partridge in a pear tree
On the tenth day of Christmas, my true love sent to me
Ten lords a-leaping
Nine ladies dancing
Eight maids a-milking
Seven swans a-swimming
Six geese a-laying
Five golden rings
Four calling birds
Three french hens
Two turtle doves and
A partridge in a pear tree
On the eleventh day of Christmas, my true love sent to me
Eleven pipers piping
Ten lords a-leaping
Nine ladies dancing
Eight maids a-milking
Seven swans a-swimming
Six geese a-laying
Five golden rings
Four calling birds
Three french hens
Two turtle doves and
A partridge in a pear tree
On the twelfth day of Christmas, my true love sent to me
Twelve drummers drumming
Eleven pipers piping
Ten lords a-leaping
Nine ladies dancing
Eight maids a-milking
Seven swans a-swimming
Six geese a-laying
Five golden rings
Four calling birds
Three french hens
Two turtle doves and
A partridge in a pear tree
Again super error message for newcomers with helpful suggestion on how to remedy the issue.
Compiling twelve_days_of_christmas v0.1.0 (/home/user/projects/my_misc/rust/learning/3/twelve_days_of_christmas) error: missing type for `const` item --> src/main.rs:1:12 | 1 | const DAYS: = ["first", "second", "third", "fourth", "fifth", "sixth", | ^ help: provide a type for the constant: `[&str; 12]` error: could not compile `twelve_days_of_christmas` (bin "twelve_days_of_christmas") due to 1 previous error
Chapter 4 - Understanding Ownership
Drop and Copy traits are mutually exclusive
Examples:
- "All the integer types, such as u32."
- "The Boolean type, bool, with values true and false."
- "All the floating-point types, such as f64."
- "The character type, char."
- "Tuples, if they only contain types that also implement Copy. For example, (i32, i32) implements Copy, but (i32, String) does not."
Functions can either take ownership, or they can borrow, specifying whether they have permission to mutate the value.
You can't borrow immutably and mutably, pick your intent
The compiler is smart, it knows when a variable isn't used anymore
let mut s = String::from("hello");
let r1 = &s; // no problem
let r2 = &s; // no problem
let r3 = &mut s; // BIG PROBLEM
println!("{r1}, {r2}, and {r3}");
let mut s = String::from("hello");
let r1 = &s; // no problem
let r2 = &s; // no problem
println!("{r1} and {r2}");
// Variables r1 and r2 will not be used after this point.
let r3 = &mut s; // no problem
println!("{r3}");
"In Rust, by contrast, the compiler guarantees that references will never be dangling references"
We cannot have a pointer to something invalid, this is handled through scope {} Perhaps we'd define the pointer first, and pass it as an arg?
4.3 - The Slice Type
Itterating a strings chars we'd call string_var.as_bytes();
fn first_word(s: &String) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s);
s.clear(); // error!
println!("the first word is: {word}");
}
Throws this error:
Compiling immutable_test v0.1.0 (/home/user/projects/my_misc/rust/learning/4/immutable_test) error[E0499]: cannot borrow `s` as mutable more than once at a time --> src/main.rs:18:5 | 16 | let word = first_word(&mut s); | ------ first mutable borrow occurs here 17 | 18 | s.clear(); // error! | ^ second mutable borrow occurs here 19 | 20 | println!("the first word is: {word}"); | ---- first borrow later used here For more information about this error, try `rustc --explain E0499`. error: could not compile `immutable_test` (bin "immutable_test") due to 1 previous error
and changing it to a mutable reference doesn't eleviate the problem either Neither does giving the first word function ownership.
Wont it cause us problems that we can't have multiple references to a var? Will it not take extra care to move around ownership and only having one reference or type of reference? What are the rules, how is it used?
This must be because &str is a more basic type, &String has more features
"The str type, also called a ‘string slice’, is the most primitive string type"
"A &str is made up of two components: a pointer to some bytes, and a length"
Chapter 5 - Using Structs to Structure Related Data
You can copy fields from another instance of an object, very peruclear, wouldn't this easily lead to errors? Copying something inadvertently, I'm sure it makes sense in it's prober use case.
let user2 = User {
email: String::from("another@example.com"),
..user1
};
There are two types of structs: regualar and tuple
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}
struct Color(i32, i32, i32);
fn main() {
let black = Color(0, 0, 0);
}
Comments